﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.IO;
using System.Xml.Linq;
using System.Data.SqlClient;
using MvcEngine.Core.Helpers;
using MvcEngine.Core.Utils;
using MvcEngine.Core.Configuration;
using MvcEngine.Core.Repositories;

using MvcEngine.Core.Content;

namespace MvcEngine.Core.Services
{
    public class ModuleInstallationService : IModuleInstallationService
    {
        #region Private fields
        private readonly string mvcExtension = ".mvc"; 
        #endregion

        #region Public methods

        public void Install(HttpPostedFileBase moduleArchive)
        {
            //LogHelper.Current.WriteInfo("Module installation started");

            string tempFolder = CreateModuleTempFolder(moduleArchive);
            ZipHelper.Current.UnpackArchive(moduleArchive.InputStream, tempFolder);

            string moduleName = GetModuleName(tempFolder);
            string moduleFolder = Path.Combine(ConfigurationUtil.Current.ModulesFolder, moduleName);
            CreateFolder(moduleFolder);

            ParseXmlAndCopyFiles(moduleFolder, tempFolder, moduleName);
        }

        //public void UnInstall(int id)
        //{
        //    IModuleManager manager = IoC.GetInstance<IModuleManager>();
        //    IModuleStartupManager startupManager = IoC.GetInstance<IModuleStartupManager>();
        //    IPageContentManager pageContentManager = IoC.GetInstance<IPageContentManager>();

        //    IModule module = manager.GetModule(id);
        //    string moduleFolder = Path.Combine(ConfigurationUtil.ModulesFolder, module.Name);

        //    XDocument doc = LoadXmlDocument(moduleFolder, module.Name);
        //    ExecuteScripts(doc, moduleFolder, "uninstall");

        //    DeleteFiles(moduleFolder, doc);
        //    startupManager.Delete(id);
        //    pageContentManager.DeleteByModule(id);
        //    manager.DeleteModule(id);
        //}


        public void UnInstall(Guid id)
        {
            IModuleRepository manager = IoC.Resolve<IModuleRepository>();
            Module module = manager.Get(id);
            string moduleFolder = Path.Combine(ConfigurationUtil.Current.ModulesFolder, module.Name);

            XDocument doc = LoadXmlDocument(moduleFolder, module.Name);

            ExecuteScripts(doc, moduleFolder, "uninstall");

            DeleteFiles(moduleFolder, doc);

            DeletePageContents(id);

            manager.Delete(id);
        }
        #endregion

        #region Private methods

        private string CreateModuleTempFolder(HttpPostedFileBase moduleArchive)
        {
            string tempModuleName = Path.GetFileNameWithoutExtension(moduleArchive.FileName);
            string tempFolder = Path.Combine(ConfigurationUtil.Current.TempFolder, tempModuleName);
            CreateFolder(tempFolder);
            return tempFolder;
        }

        private string GetModuleName(string tempFolder)
        {
            string[] files = Directory.GetFiles(tempFolder, "*" + mvcExtension);
            if (files.Length == 0 || files.Length > 1)
            {
                //Log Error and move to error page
            }

            string mvcConfigFile = files[0];
            XDocument doc = XDocument.Load(mvcConfigFile);
            return doc.Root.Element("name").Value;
        }

        private void CreateFolder(string folder)
        {
            if (Directory.Exists(folder))
                Directory.Delete(folder, true);
            Directory.CreateDirectory(folder);
        }

        private void DeletePageContents(Guid id)
        {
            IPageContentRepository pageContentManager = IoC.Resolve<IPageContentRepository>();
            pageContentManager.DeleteByModule(id);
        }

        private void DeleteFiles(string moduleFolder, XDocument doc)
        {
            if (Directory.Exists(moduleFolder))
                Directory.Delete(moduleFolder, true);

            var files = from s in doc.Root.Element("files").Elements("file")
                        select s;

            foreach (var item in files)
            {
                string file = Path.Combine(ConfigurationUtil.Current.BinFolder, item.Element("name").Value);
                switch (Path.GetExtension(file))
                {
                    case ".dll":
                        if (File.Exists(file))
                            File.Delete(file);
                        break;
                }
            }
        }

        private void ParseXmlAndCopyFiles(string moduleFolder, string tempFolder, string moduleName)
        {
            XDocument doc = LoadXmlDocument(tempFolder, moduleName);
            CopyFiles(moduleFolder, tempFolder, doc);
            Guid moduleId = CreateModule(doc);
            CreateControls(doc, moduleId);
            //CreateProviders(doc, moduleId);
            ExecuteScripts(doc, moduleFolder, "install");
        }


        private XDocument LoadXmlDocument(string path, string moduleName)
        {
            string pathToXmlFile = Path.Combine(path, moduleName + mvcExtension);
            if (!File.Exists(pathToXmlFile))
                throw new FileNotFoundException("Module xml configuration file with extension .mvc was not found!");

            XDocument doc = XDocument.Load(pathToXmlFile);
            return doc;
        } 

        #endregion

        #region Create

        private void CopyFiles(string moduleFolder, string tempFolder, XDocument doc)
        {
            var files = from s in doc.Root.Element("files").Elements("file")
                        select s;

            foreach (var item in files)
            {
                if (item.Element("folder") != null)
                {
                    string destFolder = Path.Combine(moduleFolder, item.Element("folder").Value);
                    string sourceFolder = Path.Combine(tempFolder, item.Element("folder").Value);
                    if (!Directory.Exists(destFolder))
                        Directory.CreateDirectory(destFolder);
                    CopyFile(destFolder, sourceFolder, item.Element("name").Value);
                    continue;
                }

                CopyFile(moduleFolder, tempFolder, item.Element("name").Value);
            }
        }

        private void CopyFile(string moduleFolder, string tempFolder, string fileName)
        {
            if (File.Exists(Path.Combine(moduleFolder, fileName)))
                File.Delete(Path.Combine(moduleFolder, fileName));

            File.Copy(Path.Combine(tempFolder, fileName), Path.Combine(moduleFolder, fileName));

            switch (Path.GetExtension(fileName))
            {
                case ".dll":
                    if (File.Exists(Path.Combine(ConfigurationUtil.Current.BinFolder, fileName)))
                        File.Delete(Path.Combine(ConfigurationUtil.Current.BinFolder, fileName));
                    File.Copy(Path.Combine(tempFolder, fileName), Path.Combine(ConfigurationUtil.Current.BinFolder, fileName));
                    break;
            }
        }

        //private void CreateProviders(XDocument doc, int moduleId)
        //{
        //    IDataProviderRepository repository = IoC.GetInstance<IDataProviderRepository>();

        //    foreach (var item in doc.Root.Element("providers").Elements("provider"))
        //    {
        //        IDataProvider provider = IoC.GetInstance<IDataProvider>();
        //        provider.Name = item.Attribute("name").Value;
        //        provider.ServiceType = item.Attribute("type").Value;
        //        provider.ModuleId = moduleId;
        //        repository.Add(provider);
        //    }
        //}

        private Guid CreateModule(XDocument doc)
        {
            IModuleRepository manager = IoC.Resolve<IModuleRepository>();
            Module module = IoC.Resolve<Module>();
            module.Name = doc.Root.Element("name").Value;
            module.Description = doc.Root.Element("description").Value;
            module.Version = doc.Root.Element("version").Value;
            module.CreatedDate = DateTime.Now;
            module.ModifiedDate = DateTime.Now;
            module.CreatedBy = UserContext.Current.User.Id;
            module.ModifiedBy = UserContext.Current.User.Id;
            manager.Add(module);

            return module.Id;
        }


        private void CreateControls(XDocument doc, Guid moduleId)
        {
            IControlRepository manager = IoC.Resolve<IControlRepository>();
            

            foreach (var item in doc.Root.Element("controls").Elements("control"))
            {
                Control control = IoC.Resolve<Control>();
                control.Id = Guid.NewGuid();
                control.ModuleId = moduleId;
                control.Name = item.Element("name").Value;
                control.ControlKey = item.Element("controlKey").Value;
                control.Description = item.Element("description").Value;
                control.CreatedDate = DateTime.Now;
                control.ModifiedDate = DateTime.Now;
                control.CreatedBy = UserContext.Current.User.Id;
                control.ModifiedBy = UserContext.Current.User.Id;

                manager.Add(control);
            }
        }

        //private static void CreateModuleService(XDocument doc, int moduleId)
        //{
        //    IModuleStartupRepository manager = IoC.GetInstance<IModuleStartupRepository>();

        //    var startups = from s in doc.Root.Element("startups").Elements("startup")
        //                   select s;

        //    foreach (var item in startups)
        //    {
        //        IModuleStartup moduleStartup = manager.Create();
        //        moduleStartup.Assembly = item.Element("assembly").Value.Trim();
        //        moduleStartup.Class = item.Element("class").Value.Trim();
        //        moduleStartup.Function = item.Element("function").Value.Trim();
        //        moduleStartup.ModuleId = moduleId;
        //        manager.Add(moduleStartup);
        //    }
        //}

        #endregion

        #region Execute scripts

        private void ExecuteScripts(XDocument doc, string path, string type)
        {
            var startups = from s in doc.Root.Element("scripts").Elements("script")
                           where s.Attribute("type").Value == type
                           select s;

            foreach (var item in startups)
            {
                var file = from s in doc.Root.Element("files").Elements("file")
                           where s.Element("name").Value == item.Value
                           select s;
                if (file.First().Element("folder") != null)
                {
                    ExecuteScript(item.Value, Path.Combine(path, file.First().Element("folder").Value));
                    continue;
                }

                ExecuteScript(item.Value, Path.Combine(path, item.Value));
            }
        }

        private void ExecuteScript(string name, string path)
        {
            string file = Path.Combine(path, name);
            string content = File.ReadAllText(file);
            string splitter = "GO";//TODO: change this to GO\t\n
            string[] scripts = content.Split(new string[] { splitter }, StringSplitOptions.None);

            using (SqlConnection con = new SqlConnection(ConfigurationUtil.Current.ConnectionString))
            {
                con.Open();
                for (int i = 0; i <= scripts.Length - 2; i++)
                {
                    SqlCommand cmd = new SqlCommand(scripts[i], con);
                    cmd.ExecuteNonQuery();
                }
            }
        }

        #endregion
    }
}
